
// the BytePerSector is a constans in all regions
// the region can change the SectorPerBlock!
// the block region report is the real physical block
// the physical sector used by FAL is the "region combine", not the realy physical sector
//so  the physical sector used by FAL looks continued event the regions is not continued
// for FMD sector size is constans in all regions

#include "falmain.h"
#include "VirtualDisk.h"

extern DWORD g_dwWriteVerify;
extern DWORD g_dwReadRetry;
static BYTE* s_pbVerifyBuf=NULL;

#define VDKS_MUTEXT_NAME	TEXT("VDSK_MUTEX")
static HANDLE s_hMutex;
static DWORD s_dwVirtDskId;
static DWORD s_dwDskStartBlk;
static DWORD s_dwDskBlkNumb;
static DWORD *s_pdwDskBadBlk = NULL;
static DWORD s_dwDskBadBlkNumb;

//export functions
BOOL  FMDHOOK_ReadCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff);
BOOL  FMDHOOK_WriteCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff);
BOOL FMDHOOK_SetAutoCap(BOOL fAutoCap);
int FMDHOOK_GetLastReadErrCode(SECTOR_ADDR errSectorAddr);


PVOID  FMDHOOK_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut);
BOOL  FMDHOOK_Deinit(PVOID pv);
BOOL  FMDHOOK_GetInfo(PFlashInfo pFlashInfo);
BOOL  FMDHOOK_GetInfoEx(PFlashInfoEx pFlashInfo, PDWORD pdwNumRegions);
BOOL  FMDHOOK_ReadSector (SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors);
BOOL  FMDHOOK_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors);
BOOL  FMDHOOK_EraseBlock(BLOCK_ID blockID);
VOID FMDHOOK_GetPhysSectorAddr (DWORD dwSector, PSECTOR_ADDR pStartSectorAddr);
BOOL FMDHOOK_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus);
DWORD FMDHOOK_GetBlockStatus(BLOCK_ID blockID);
BOOL FMDHOOK_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned);

BOOL  RWCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, BOOL fRead);
BOOL TransHookSectorAddr(SECTOR_ADDR sectorAddrFAL, DWORD dwNumSectors, SECTOR_ADDR* psectorAddrFMD, DWORD* pdwRegion, BOOL* pfIsLastSectOfBlck);
DWORD GetBlockStartSect(BLOCK_ID blockID);
int FMD_ReadSectorAkVerify(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors);
BOOL FMD_WriteSectorAkVerify(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors);

PVOID  SyncFMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut);
BOOL  SyncFMD_Deinit(PVOID pv);
BOOL  SyncFMD_GetInfo(PFlashInfo pFlashInfo);
BOOL  SyncFMD_GetInfoEx(PFlashInfoEx pFlashInfo, PDWORD pdwNumRegions);
BOOL  SyncFMD_ReadSector (SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors);
BOOL  SyncFMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors);
int SyncFMD_ReadSectorAk(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors);
BOOL  SyncFMD_WriteSectorAk (SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors);
VOID SyncFMD_GetPhysSectorAddr (DWORD dwSector, PSECTOR_ADDR pStartSectorAddr);
BOOL SyncFMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus);
DWORD SyncFMD_GetBlockStatus(BLOCK_ID blockID);
BOOL  SyncFMD_EraseBlock(BLOCK_ID blockID);
BOOL SyncFMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned);

// notify to sync with compact.h
#ifndef MINIMUM_FLASH_BLOCKS_TO_RESERVE
#define MINIMUM_FLASH_BLOCKS_TO_RESERVE				5
#endif

static FMDInterface v_FMDInterface={0};     // the real interface of FMD
static FlashInfoEx* v_pFlashInfoEx=NULL;    // modified for reserve cap
static DWORD* v_pdwRegionStartSectS=NULL;   // make region search ease 
static DWORD* v_pdwOldRegionStartSectS=NULL;// make region search ease 
static BOOL v_fAutoCap=TRUE;

static int v_iFMDLastReadErr=0;
static SECTOR_ADDR v_FMDLastReadErrSect=UNMAPPED_LOGICAL_SECTOR;

VOID DumpFlashInfoEx(FlashInfoEx* pInfoEx)
{
    RETAILMSG(1, (TEXT("FlashInfoEx\r\n")));
    RETAILMSG(1, (TEXT("  dwNumBlocks:0x%x, dwDataBytesPerSector:0x%x, dwNumRegions:0x%x\r\n"), pInfoEx->dwNumBlocks, pInfoEx->dwDataBytesPerSector, pInfoEx->dwNumRegions));
    for (DWORD i=0; i<pInfoEx->dwNumRegions; i++)
    {
        FlashRegion* pregion=&pInfoEx->region[i];
        RETAILMSG(1, (TEXT("    region[%d]:\r\n"), i));
        RETAILMSG(1, (TEXT("      regionType:%d, dwStartPhysBlock:0x%x, dwNumPhysBlocks:0x%x\r\n"), pregion->regionType, pregion->dwStartPhysBlock, pregion->dwNumPhysBlocks));
        RETAILMSG(1, (TEXT("      dwNumLogicalBlocks:0x%x, dwSectorsPerBlock:0x%x, dwBytesPerBlock:0x%x\r\n"), pregion->dwNumLogicalBlocks, pregion->dwSectorsPerBlock, pregion->dwBytesPerBlock));
    }
}

VOID DumpRegionStartSectS(DWORD* pdwRegStartSectS, DWORD dwSize)
{
    RETAILMSG(1, (TEXT("DumpRegionStartSectS:\r\n")));
    for (DWORD i=0; i<dwSize; i++)
    {
        RETAILMSG(1, (TEXT("  RegionStartSect[%d]:0x%x"), i, pdwRegStartSectS[i]));
    }
    RETAILMSG(1, (TEXT("\r\n")));
}


LPVOID FMDHOOK_HookInterface(PFMDInterface pInterface)
{
    v_FMDInterface=*pInterface;

    pInterface->pInit=FMDHOOK_Init;
    pInterface->pDeInit=FMDHOOK_Deinit;
    pInterface->pGetInfo=FMDHOOK_GetInfo;
    pInterface->pGetBlockStatus=FMDHOOK_GetBlockStatus;
    pInterface->pSetBlockStatus=FMDHOOK_SetBlockStatus;
    pInterface->pReadSector=FMDHOOK_ReadSector;
    pInterface->pWriteSector=FMDHOOK_WriteSector;
    pInterface->pEraseBlock=FMDHOOK_EraseBlock;
//	pInterface->pPowerUp=v_FMDInterface->pPowerUp;
//	pInterface->pPowerDown=v_FMDInterface->pPowerDown;
    pInterface->pGetPhysSectorAddr=FMDHOOK_GetPhysSectorAddr;
    pInterface->pGetInfoEx=FMDHOOK_GetInfoEx;		//! actuly we not support region for now
    pInterface->pOEMIoControl=FMDHOOK_OEMIoControl;


    return (LPVOID)&v_FMDInterface;
}

void FMDHOOK_UnhookInterface(LPVOID pContext, FMDInterface *pInterface)
{
    if (v_pFlashInfoEx)
    {
        LocalFree(v_pFlashInfoEx);
        v_pFlashInfoEx=NULL;
    }

    if (v_pdwRegionStartSectS)
    {
        LocalFree(v_pdwRegionStartSectS);
        v_pdwRegionStartSectS=NULL;
    }
}

PVOID  FMDHOOK_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
    s_hMutex=CreateMutex( NULL, FALSE, VDKS_MUTEXT_NAME);
    ASSERT(NULL!=s_hMutex);

    // if we are the second virtual disk
    if (ERROR_ALREADY_EXISTS==GetLastError())
    {
        s_dwVirtDskId=1;
    }
    else
    {
        s_dwVirtDskId=0;
    }

    PVOID pvRet=SyncFMD_Init(lpActiveReg, pRegIn, pRegOut);
    if (NULL==pvRet)
    {
        return NULL;
    }

    VIRTUAL_DISK_INFO VDInfo;
    if ( (FALSE==GetVirtualDskInfo(s_dwVirtDskId, &VDInfo))
            || (0==VDInfo.BlockNum) )
    {
        RETAILMSG(1, (TEXT("Can't get VirtualDskInfo for Vdisk :%d!!!\r\n"), s_dwVirtDskId));
        return NULL;
    }
    s_dwDskStartBlk = VDInfo.StartBlock;
    s_dwDskBlkNumb = VDInfo.BlockNum;
    s_pdwDskBadBlk = VDInfo.BadBlock;
    s_dwDskBadBlkNumb = VDInfo.BadBlockNum;
    RETAILMSG(1, (TEXT("Vdisk :%d, s_dwDskStartBlk:0x%x, s_dwDskBlkNumb:0x%x, "), s_dwVirtDskId, s_dwDskStartBlk, s_dwDskBlkNumb));
    RETAILMSG(1, (TEXT("s_pdwDskBadBlk:0x%x, s_dwDskBadBlkNumb:0x%x!\r\n"), s_pdwDskBadBlk, s_dwDskBadBlkNumb));

    // init the region info
    FMDHOOK_GetInfoEx(NULL, NULL);

    if (!s_pbVerifyBuf)
    {
        s_pbVerifyBuf=(BYTE*)LocalAlloc(LPTR, v_pFlashInfoEx->dwDataBytesPerSector);
        if (!s_pbVerifyBuf)
        {
            SyncFMD_Deinit(pvRet);
            return NULL;
        }
    }

    return pvRet;
}

BOOL  FMDHOOK_Deinit(PVOID pv)
{
    if (s_pbVerifyBuf)
    {
        LocalFree(s_pbVerifyBuf);
        s_pbVerifyBuf=NULL;
    }

    BOOL ret=SyncFMD_Deinit(pv);

    if (NULL!=s_hMutex)
    {
        CloseHandle(s_hMutex);
        s_hMutex=NULL;
    }

    if (v_pFlashInfoEx)
    {
        LocalFree(v_pFlashInfoEx);
        v_pFlashInfoEx=NULL;
    }

    if(v_pdwRegionStartSectS)
    {
        LocalFree(v_pdwRegionStartSectS);
        v_pdwRegionStartSectS=NULL;
    }

    if (s_pdwDskBadBlk)
    {
        LocalFree(s_pdwDskBadBlk);
        s_pdwDskBadBlk = NULL;
    }
    return ret;
}

BOOL  FMDHOOK_GetInfo(PFlashInfo pFlashInfo)
{
    ASSERT(NULL!=v_pFlashInfoEx);

    pFlashInfo->flashType=v_pFlashInfoEx->flashType;
    pFlashInfo->dwNumBlocks=v_pFlashInfoEx->dwNumBlocks;
    pFlashInfo->dwBytesPerBlock=v_pFlashInfoEx->dwDataBytesPerSector*v_pFlashInfoEx->region[0].dwSectorsPerBlock;
    pFlashInfo->wSectorsPerBlock=(WORD)v_pFlashInfoEx->region[0].dwSectorsPerBlock;
    pFlashInfo->wDataBytesPerSector=(WORD)v_pFlashInfoEx->dwDataBytesPerSector;

    return TRUE;
}

BOOL  FMDHOOK_GetInfoEx(PFlashInfoEx pFlashInfo, PDWORD pdwNumRegions)
{
    if (NULL==v_pFlashInfoEx)
    {
        DWORD dwNumRegions = 0;
        if( v_FMDInterface.pGetInfoEx
            && v_FMDInterface.pGetInfoEx(NULL, &dwNumRegions)
            && (dwNumRegions != 0) )
        {
            v_pFlashInfoEx = (PFlashInfoEx)LocalAlloc (LPTR
                    , sizeof(FlashInfoEx) + (dwNumRegions - 1) * sizeof(FlashRegion));
            if (v_pFlashInfoEx)
            {
                v_pFlashInfoEx->cbSize=sizeof(FlashInfoEx);
                if(!v_FMDInterface.pGetInfoEx(v_pFlashInfoEx, &dwNumRegions))
                {
                   LocalFree(v_pFlashInfoEx);
                   v_pFlashInfoEx=NULL;
                }
            }

            //check if this region is in the virtual disk
            DWORD dwNumbVDiskRegion=0, dwNumbBlock=0;
            FlashRegion* pFlashRegion = (FlashRegion*)LocalAlloc (LPTR, (dwNumRegions) * sizeof(FlashRegion));
            for(DWORD i=0; i<dwNumRegions; i++)
            {
                if( (v_pFlashInfoEx->region[i].dwStartPhysBlock>=s_dwDskStartBlk)
                    && ((v_pFlashInfoEx->region[i].dwStartPhysBlock+v_pFlashInfoEx->region[i].dwNumPhysBlocks)<=s_dwDskStartBlk+s_dwDskBlkNumb) )
                {
                    pFlashRegion[dwNumbVDiskRegion]=v_pFlashInfoEx->region[i];
                    dwNumbVDiskRegion++;
                    dwNumbBlock+=v_pFlashInfoEx->region[i].dwNumPhysBlocks;
                }
            }

            memcpy(v_pFlashInfoEx->region, pFlashRegion, dwNumbVDiskRegion*sizeof(FlashRegion));
            v_pFlashInfoEx->dwNumRegions=dwNumbVDiskRegion;
            v_pFlashInfoEx->dwNumBlocks=dwNumbBlock;
            LocalFree(pFlashRegion);
        }
        else
        {
            // GetInfoEx not implement tread the whole virtual disk as one region
            FlashInfo fi;
            SyncFMD_GetInfo(&fi);

            v_pFlashInfoEx = (PFlashInfoEx)LocalAlloc (LPTR, sizeof(FlashInfoEx)); // one dword for region start sect
            if (v_pFlashInfoEx)
            {
                v_pFlashInfoEx->cbSize=sizeof(FlashInfoEx);
                v_pFlashInfoEx->flashType = fi.flashType;
//                v_pFlashInfoEx->dwNumBlocks = fi.dwNumBlocks;
                v_pFlashInfoEx->dwNumBlocks = s_dwDskBlkNumb;
                v_pFlashInfoEx->dwDataBytesPerSector = fi.wDataBytesPerSector;
                v_pFlashInfoEx->dwNumRegions = 1;

                v_pFlashInfoEx->region[0].regionType = FILESYS;
//                v_pFlashInfoEx->region[0].dwStartPhysBlock = 0;
//                v_pFlashInfoEx->region[0].dwNumPhysBlocks = fi.dwNumBlocks;
                v_pFlashInfoEx->region[0].dwStartPhysBlock = s_dwDskStartBlk;
                v_pFlashInfoEx->region[0].dwNumPhysBlocks = s_dwDskBlkNumb;
                v_pFlashInfoEx->region[0].dwNumLogicalBlocks = FIELD_NOT_IN_USE;    // this means it will be caculate later
                v_pFlashInfoEx->region[0].dwSectorsPerBlock = fi.wSectorsPerBlock;
                v_pFlashInfoEx->region[0].dwBytesPerBlock = fi.dwBytesPerBlock;
                v_pFlashInfoEx->region[0].dwCompactBlocks = MINIMUM_FLASH_BLOCKS_TO_RESERVE;
            }

        }
        RETAILMSG(1, (TEXT("FlashInfoEx original\r\n")));
        DumpFlashInfoEx(v_pFlashInfoEx);

        // modify the flashinfoex
        if (v_pFlashInfoEx)
        {
            //reserve one sect for cap
            ASSERT(!v_pdwRegionStartSectS);

            v_pdwRegionStartSectS=(DWORD*)LocalAlloc(LPTR, sizeof(DWORD)*(v_pFlashInfoEx->dwNumRegions+1)*2);
            if (!v_pdwRegionStartSectS)
            {
                LocalFree(v_pFlashInfoEx);
                v_pFlashInfoEx=NULL;
            }
            v_pdwOldRegionStartSectS=v_pdwRegionStartSectS+(v_pFlashInfoEx->dwNumRegions+1);

            // init old region sector info
            v_pdwOldRegionStartSectS[0]=v_pFlashInfoEx->region[0].dwStartPhysBlock*v_pFlashInfoEx->region[0].dwSectorsPerBlock;
            DEBUGMSG(v_pdwOldRegionStartSectS[0]!=0, (TEXT("region[0] start sector:0x%x!\r\n"), v_pdwOldRegionStartSectS[0]));
            for (DWORD i=1; i<=v_pFlashInfoEx->dwNumRegions; i++)
            {
                v_pdwOldRegionStartSectS[i]= v_pdwOldRegionStartSectS[i-1]+v_pFlashInfoEx->region[i-1].dwNumPhysBlocks*v_pFlashInfoEx->region[i-1].dwSectorsPerBlock;
            }

            // modify flashinfoex
            for (DWORD i=0; i<v_pFlashInfoEx->dwNumRegions; i++)
            {
                ASSERT(v_pFlashInfoEx->region[i].dwNumLogicalBlocks == FIELD_NOT_IN_USE);   // if not FIELD_NOT_IN_USE FMD seems to set more field, and i'am not sure

                v_pFlashInfoEx->region[i].dwSectorsPerBlock--;      // reserve for cap
                v_pFlashInfoEx->region[i].dwBytesPerBlock-=v_pFlashInfoEx->dwDataBytesPerSector;
            }
            RETAILMSG(1, (TEXT("FlashInfoEx modified\r\n")));
            DumpFlashInfoEx(v_pFlashInfoEx);

            // init modified region sector info
            v_pdwRegionStartSectS[0]=v_pFlashInfoEx->region[0].dwStartPhysBlock*v_pFlashInfoEx->region[0].dwSectorsPerBlock;
            for (DWORD i=1; i<=v_pFlashInfoEx->dwNumRegions; i++)
            {
                v_pdwRegionStartSectS[i]= v_pdwRegionStartSectS[i-1]+v_pFlashInfoEx->region[i-1].dwNumPhysBlocks*v_pFlashInfoEx->region[i-1].dwSectorsPerBlock;
            }

            RETAILMSG(1, (TEXT("start sectors for original region\r\n")));
            DumpRegionStartSectS(v_pdwOldRegionStartSectS, v_pFlashInfoEx->dwNumRegions+1);
            RETAILMSG(1, (TEXT("start sectors for modified region\r\n")));
            DumpRegionStartSectS(v_pdwRegionStartSectS, v_pFlashInfoEx->dwNumRegions+1);

        }
        else
        {
            return FALSE;
        }
    }


    if (NULL==pdwNumRegions)
    {
        return FALSE;
    }

    if (*pdwNumRegions < v_pFlashInfoEx->dwNumRegions)
    {
        *pdwNumRegions = v_pFlashInfoEx->dwNumRegions;
        return TRUE;
    }

    *pdwNumRegions=v_pFlashInfoEx->dwNumRegions;
    memcpy(pFlashInfo, v_pFlashInfoEx, sizeof(FlashInfoEx) + (v_pFlashInfoEx->dwNumRegions - 1) * sizeof(FlashRegion));

    return TRUE;
}


BOOL  FMDHOOK_ReadSector (SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    ASSERT(1==dwNumSectors);

    SECTOR_ADDR sectorAddrFMD;

    if (TransHookSectorAddr(startSectorAddr, dwNumSectors, &sectorAddrFMD, NULL, NULL))
    {
        if (FMD_ReadSectorAkVerify(sectorAddrFMD, pSectorBuff, (PSectorInfoAk)pSectorInfoBuff, 1))
        {
            return TRUE;
        }
        DEBUGMSG(1, (TEXT("!!! ReadSector:0x%x Error !!!\r\n"), sectorAddrFMD));
    }

    v_FMDLastReadErrSect=startSectorAddr;

    return FALSE;
}

BOOL  FMDHOOK_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    ASSERT(1==dwNumSectors);

    SECTOR_ADDR sectorAddrFMD;
    DWORD dwRegion;
    BOOL fLastSectOfBlock;
    BOOL fRet=FALSE;
    if (TransHookSectorAddr(startSectorAddr, dwNumSectors, &sectorAddrFMD, &dwRegion, &fLastSectOfBlock))
    {
        if (FMD_WriteSectorAkVerify(sectorAddrFMD, pSectorBuff, (PSectorInfoAk)pSectorInfoBuff, 1))
        {
            fRet=TRUE;
        }

        DEBUGMSG(!fRet, (TEXT("!!! WriteSector:0x%x Error !!!\r\n"), sectorAddrFMD));
    }

    if ( fRet && v_fAutoCap && fLastSectOfBlock)
    {
        PSectorInfoAk  psectorInfoS=(PSectorInfoAk)LocalAlloc(LPTR, v_pFlashInfoEx->dwDataBytesPerSector);
        if (!psectorInfoS)
        {
            return FALSE;
        }

        // read from data sectors
        sectorAddrFMD-=(v_pFlashInfoEx->region[dwRegion].dwSectorsPerBlock-1);
        for (DWORD i=0; i<v_pFlashInfoEx->region[dwRegion].dwSectorsPerBlock; i++, sectorAddrFMD++)
        {
            if (!FMD_ReadSectorAkVerify(sectorAddrFMD, NULL, psectorInfoS+i, 1))
            {
                DEBUGMSG(1, (TEXT("!!! ReadSector:0x%x for cap Error !!!\r\n"), sectorAddrFMD));
                fRet=FALSE;
                break;
            }
        }

        if (fRet)
        {
            // write to cap sector
            fRet=FALSE;
            SectorMappingInfo mapInfo;
            memset(&mapInfo, 0xff, sizeof(mapInfo));
            mapInfo.logicalSectorAddr=CAP_SECT_SIG;
            if (FMD_WriteSectorAkVerify(sectorAddrFMD, (LPBYTE)psectorInfoS, (PSectorInfoAk)&mapInfo, 1))
            {
                fRet=TRUE;
            }

            DEBUGMSG(!fRet, (TEXT("!!! WriteSector:0x%x for cap Error !!!\r\n"), sectorAddrFMD));
        }

        LocalFree(psectorInfoS);
    }

    return fRet;
}


BOOL  FMDHOOK_EraseBlock(BLOCK_ID blockID)
{
    DEBUGMSG(ZONE_LOG, (TEXT("erase block:0x%x!\r\n"), blockID));
    return SyncFMD_EraseBlock(blockID);
}

BOOL FMDHOOK_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
    // make cap invalid also
    SectorMappingInfo mapInfo;
    memset(&mapInfo, 0, sizeof(mapInfo));
    if( (!SyncFMD_SetBlockStatus(blockID, dwStatus))
        || (!RWCapSector (blockID, NULL, (PSectorInfo)&mapInfo, FALSE)) )
    {
        DEBUGMSG(1, (TEXT("!!! FMDHOOK_SetBlockStatus block:0x%x Error, try erase and set bad!!!\r\n"), blockID));
        SyncFMD_EraseBlock(blockID);
        SyncFMD_SetBlockStatus(blockID, BLOCK_STATUS_BAD);
    }

    if (dwStatus!=SyncFMD_GetBlockStatus(blockID))
    {
        DEBUGMSG(1, (TEXT("!!! FMDHOOK_SetBlockStatus block:0x%x check back Error, try erase and set bad!!!\r\n"), blockID));
        SyncFMD_EraseBlock(blockID);
        SyncFMD_SetBlockStatus(blockID, BLOCK_STATUS_BAD);
        return FALSE;
    }
    
    return TRUE;
}

DWORD FMDHOOK_GetBlockStatus(BLOCK_ID blockID)
{
    // search in bad block list
    if (s_pdwDskBadBlk)
    {
        DWORD *pBlock=s_pdwDskBadBlk;
        for (DWORD i=0; i<s_dwDskBadBlkNumb; i++)
        {
            if (blockID == *pBlock++)
            {//found
                RETAILMSG(0, (TEXT("=== FMDHOOK_GetBlockStatus: find block 0x%x in bad block list\r\n"), blockID));
                return BLOCK_STATUS_BAD;
            }
        }
    }

    return SyncFMD_GetBlockStatus(blockID);
}

BOOL FMDHOOK_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
    return SyncFMD_OEMIoControl(dwIoControlCode, pInBuf, nInBufSize, pOutBuf, nOutBufSize, pBytesReturned);
}

// get the real physical sector addr, FMD_GetPhysSectorAddr transfer FAL sector addr to real physcial addr
VOID FMDHOOK_GetPhysSectorAddr (DWORD dwSector, PSECTOR_ADDR pStartSectorAddr)
{
    *pStartSectorAddr=UNMAPPED_LOGICAL_SECTOR;
    if (v_FMDInterface.pGetPhysSectorAddr)
    {
        DWORD sectorAddrFMD;
        if (TransHookSectorAddr(dwSector, 1, &sectorAddrFMD, NULL, NULL))
        {
            SyncFMD_GetPhysSectorAddr(sectorAddrFMD, pStartSectorAddr);
            if (UNMAPPED_LOGICAL_SECTOR!=*pStartSectorAddr)
            {
                *pStartSectorAddr;   // for we combine 2 sector as one
            }
        }
    }
}

BOOL FMDHOOK_SetAutoCap(BOOL fAutoCap)
{
    BOOL fOldStat=v_fAutoCap;
    v_fAutoCap=fAutoCap;
    return v_fAutoCap;
}


// read the cap sector of the block which sectorAddr reside in
BOOL  FMDHOOK_ReadCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff)
{
    // search in bad block list
    if (s_pdwDskBadBlk)
    {
        DWORD *pBlock=s_pdwDskBadBlk;
        for (DWORD i=0; i<s_dwDskBadBlkNumb; i++)
        {
            if (dwBlockID == *pBlock++)
            {//found
                RETAILMSG(0, (TEXT("=== FMDHOOK_ReadCapSector: find block 0x%x in bad block list\r\n"), dwBlockID));
                //mark as valid cap, and set bad
                pSectorInfoBuff->dwReserved1 = CAP_SECT_SIG;
                ((PSectorInfo)pSectorBuff)->bBadBlock = 0;
                return TRUE;
            }
        }
    }
    
    return RWCapSector(dwBlockID, pSectorBuff, pSectorInfoBuff, TRUE);
}

// write the cap sector of the block which sectorAddr reside in
BOOL  FMDHOOK_WriteCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff)
{
    return RWCapSector(dwBlockID, pSectorBuff, pSectorInfoBuff, FALSE);
}

int FMDHOOK_GetLastReadErrCode(SECTOR_ADDR errSectorAddr)
{
    if (errSectorAddr!=v_FMDLastReadErrSect)
    {
        DEBUGMSG(ZONE_WARNING, (TEXT("!!! FMDHOOK_GetLastReadErrCode get sector:0x%x not the last read err sector:0x%x  bOEMReserved:0x%x !!!\r\n"), errSectorAddr, v_FMDLastReadErrSect));
        return 0;
    }
    return v_iFMDLastReadErr;
}

DWORD FMDHOOKInternal_GetBlockStatus(BLOCK_ID blockID, SectorInfo* pSI)
{
    // search in bad block list
    if (s_pdwDskBadBlk)
    {
        DWORD *pBlock=s_pdwDskBadBlk;
        for (DWORD i=0; i<s_dwDskBadBlkNumb; i++)
        {
            if (blockID == *pBlock++)
            {//found
                RETAILMSG(0, (TEXT("=== FMDHOOK_GetBlockStatus: find block 0x%x in bad block list\r\n"), blockID));
                return BLOCK_STATUS_BAD;
            }
        }
    }

    if(!pSI)
    {
        return 0;
    }

    if (0xff!=pSI->bBadBlock)
    {
        return BLOCK_STATUS_BAD;
    }

    DWORD ret=0;
    if (!(pSI->bOEMReserved & OEM_BLOCK_READONLY))
    {
        ret |= BLOCK_STATUS_READONLY;
    }
    if (!(pSI->bOEMReserved & OEM_BLOCK_RESERVED))  
    {
        ret |= BLOCK_STATUS_RESERVED;
    }

    return ret;
}


BOOL  RWCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, BOOL fRead)
{
    SECTOR_ADDR sectorAddrFMD;

    for (DWORD i=0; i<v_pFlashInfoEx->dwNumRegions; i++)
    {
        if ( (dwBlockID>=v_pFlashInfoEx->region[i].dwStartPhysBlock)
                && (dwBlockID<(v_pFlashInfoEx->region[i].dwStartPhysBlock+v_pFlashInfoEx->region[i].dwNumPhysBlocks)) )
        {
            dwBlockID-=v_pFlashInfoEx->region[i].dwStartPhysBlock;
            sectorAddrFMD=v_pdwOldRegionStartSectS[i]+(dwBlockID+1)*(v_pFlashInfoEx->region[i].dwSectorsPerBlock+1);
            sectorAddrFMD-=1;

            if ( ( fRead && (FMD_ReadSectorAkVerify(sectorAddrFMD, pSectorBuff, (PSectorInfoAk)pSectorInfoBuff, 1)) )
                    ||( (!fRead) && (FMD_WriteSectorAkVerify(sectorAddrFMD, pSectorBuff, (PSectorInfoAk)pSectorInfoBuff, 1))) )
            {
                return TRUE;
            }

            DEBUGMSG(1, (TEXT("!!! %s:0x%x for RWCapSector Error !!!\r\n"), fRead?TEXT("ReadSector"):TEXT("WriteSector"), sectorAddrFMD));
            break;
        }
    }

    return FALSE;
}

// transfor hooked FAL physical sector address to FMD physical sector address
BOOL TransHookSectorAddr(SECTOR_ADDR sectorAddrFAL, DWORD dwNumSectors, SECTOR_ADDR* psectorAddrFMD, DWORD* pdwRegion, BOOL* pfIsLastSectOfBlck)
{
    // find region
    for (DWORD dwRegionId=0; dwRegionId<v_pFlashInfoEx->dwNumRegions; dwRegionId++)
    {
        if ( (sectorAddrFAL>=v_pdwRegionStartSectS[dwRegionId])
                && (sectorAddrFAL<v_pdwRegionStartSectS[dwRegionId+1])
                && ( (sectorAddrFAL+dwNumSectors)<=v_pdwRegionStartSectS[dwRegionId+1] ) )
        {
            if (pdwRegion)
            {
                *pdwRegion=dwRegionId;
            }

            DWORD dwRegionSectOffet=sectorAddrFAL-v_pdwRegionStartSectS[dwRegionId];
            DWORD dwBlock=dwRegionSectOffet/v_pFlashInfoEx->region[dwRegionId].dwSectorsPerBlock;

            if (pfIsLastSectOfBlck)
            {
                if (0==(dwRegionSectOffet+1)%v_pFlashInfoEx->region[dwRegionId].dwSectorsPerBlock)
                {
                    *pfIsLastSectOfBlck=TRUE;
                }
                else
                {
                    *pfIsLastSectOfBlck=FALSE;
                }
            }

            // find FMD physical sect
            *psectorAddrFMD=v_pdwOldRegionStartSectS[dwRegionId];
            *psectorAddrFMD+=dwBlock*(v_pFlashInfoEx->region[dwRegionId].dwSectorsPerBlock+1);
            *psectorAddrFMD+=(dwRegionSectOffet%v_pFlashInfoEx->region[dwRegionId].dwSectorsPerBlock);

            return TRUE;
        }
    }

    DEBUGMSG(1, (TEXT("!!! TransHookSectorAddr sectorAddrFAL:0x%x Error !!!\r\n"), sectorAddrFAL));
    return FALSE;
}

DWORD GetBlockStartSect(BLOCK_ID blockID)
{
    DWORD dwRegionId;
    for (dwRegionId=v_pFlashInfoEx->dwNumRegions; dwRegionId>0; dwRegionId--)
    {
        if (blockID>=v_pFlashInfoEx->region[dwRegionId-1].dwStartPhysBlock)
        {
            dwRegionId--;
            DWORD dwPhySectPerBlock=(v_pFlashInfoEx->region[dwRegionId].dwSectorsPerBlock+1);
            SECTOR_ADDR sectAddr=v_pdwOldRegionStartSectS[dwRegionId];
            sectAddr+=(blockID-v_pFlashInfoEx->region[dwRegionId].dwStartPhysBlock)*dwPhySectPerBlock;

            return sectAddr;
        }
    }

    ASSERT(FALSE);
    return 0xffffffff;
}

BOOL FMD_WriteSectorAkVerify(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors)
{
	BOOL fRet=FALSE;
	
	ASSERT(1==dwNumSectors);
	if(SyncFMD_WriteSectorAk(sectorAddrFMD, pSectorBuff, pSectorInfoBuff, dwNumSectors))
	{
		fRet=TRUE;
		if(1==g_dwWriteVerify)
		{
			SectorInfoAk siAk;

            if (FMDAK_READ_RET_OK != SyncFMD_ReadSectorAk(sectorAddrFMD, s_pbVerifyBuf, &siAk, dwNumSectors))
            {
                DEBUGMSG(ZONE_ERROR, (TEXT("!!! FMD_WriteSectorAkVerify:0x%x veriry read Error !!!\r\n"), sectorAddrFMD));
                fRet=FALSE;
            }

            if(pSectorBuff && (memcmp(s_pbVerifyBuf, pSectorBuff, v_pFlashInfoEx->dwDataBytesPerSector)))
            {
                DEBUGMSG(ZONE_ERROR, (TEXT("!!! FMD_WriteSectorAkVerify:0x%x veriry Error !!!\r\n"), sectorAddrFMD));
                fRet=FALSE;
            }

            if(pSectorInfoBuff && (memcmp(&siAk, pSectorInfoBuff, sizeof(siAk))))
            {
                DEBUGMSG(ZONE_ERROR, (TEXT("!!! FMD_WriteSectorAkVerify:0x%x veriry sector info Error !!!\r\n"), sectorAddrFMD));
                fRet=FALSE;
            }
        }
    }

    return fRet;
}

BOOL FMD_ReadSectorAkVerify(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors)
{
    ASSERT(1==dwNumSectors);

    int iRdRet;

	for(DWORD i=0; i<=g_dwReadRetry; i++)
	{
		iRdRet=SyncFMD_ReadSectorAk(sectorAddrFMD, pSectorBuff, (PSectorInfoAk)pSectorInfoBuff, 1);
		if(FMDAK_READ_RET_OK==iRdRet)
		{
			DEBUGMSG((0!=i), (TEXT("!!! ReadSector:0x%x ok until the %i try!!!\r\n"), sectorAddrFMD, i));
			return TRUE;
		}
	}
    RETAILMSG(1, (TEXT("!!! ReadSector:0x%x Error !!!\r\n"), sectorAddrFMD));

    v_iFMDLastReadErr=iRdRet;

    return FALSE;
}

PVOID  SyncFMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    PVOID ret=v_FMDInterface.pInit(lpActiveReg, pRegIn, pRegOut);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL  SyncFMD_Deinit(PVOID pv)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pDeInit(pv);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL  SyncFMD_GetInfo(PFlashInfo pFlashInfo)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pGetInfo(pFlashInfo);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL  SyncFMD_GetInfoEx(PFlashInfoEx pFlashInfo, PDWORD pdwNumRegions)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pGetInfoEx(pFlashInfo, pdwNumRegions);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL  SyncFMD_ReadSector (SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pReadSector (startSectorAddr, pSectorBuff, pSectorInfoBuff, dwNumSectors);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL  SyncFMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pWriteSector(startSectorAddr, pSectorBuff, pSectorInfoBuff, dwNumSectors);
    ReleaseMutex(s_hMutex);
    return ret;
}

int SyncFMD_ReadSectorAk(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors)
{
	WaitForSingleObject(s_hMutex, INFINITE);
	int ret=FMD_ReadSectorAk(sectorAddrFMD, pSectorBuff, pSectorInfoBuff, dwNumSectors);
	ReleaseMutex(s_hMutex);
	return ret;
}

BOOL  SyncFMD_WriteSectorAk (SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors)
{
	WaitForSingleObject(s_hMutex, INFINITE);
	BOOL ret=FMD_WriteSectorAk(sectorAddrFMD, pSectorBuff, pSectorInfoBuff, dwNumSectors);
	ReleaseMutex(s_hMutex);
	return ret;
}

VOID SyncFMD_GetPhysSectorAddr (DWORD dwSector, PSECTOR_ADDR pStartSectorAddr)
{
    if(!v_FMDInterface.pGetPhysSectorAddr)
    {
        return;
    }

    WaitForSingleObject(s_hMutex, INFINITE);
    v_FMDInterface.pGetPhysSectorAddr(dwSector, pStartSectorAddr);
    ReleaseMutex(s_hMutex);
}

BOOL SyncFMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pSetBlockStatus(blockID, dwStatus);
    ReleaseMutex(s_hMutex);
    return ret;
}

DWORD SyncFMD_GetBlockStatus(BLOCK_ID blockID)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    DWORD ret=v_FMDInterface.pGetBlockStatus(blockID);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL  SyncFMD_EraseBlock(BLOCK_ID blockID)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pEraseBlock(blockID);
    ReleaseMutex(s_hMutex);
    return ret;
}

BOOL SyncFMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
    WaitForSingleObject(s_hMutex, INFINITE);
    BOOL ret=v_FMDInterface.pOEMIoControl(dwIoControlCode, pInBuf, nInBufSize, pOutBuf, nOutBufSize, pBytesReturned);
    ReleaseMutex(s_hMutex);
    return ret;
}

